/**
 * @file OTAUpdate.cpp
 * @brief OTA更新库的实现文件
 */

#include "OTAUpdate.h"
#include <LittleFS.h>

/**
 * @brief 构造函数实现
 * @param ssid WiFi网络名称
 * @param password WiFi网络密码
 * @param updateServer OTA更新服务器地址
 * @param versionCheckUrl 版本检查URL
 * @param currentVersion 当前固件版本
 */
OTAUpdate::OTAUpdate(const char* ssid, const char* password, const char* updateServer, 
                     const char* versionCheckUrl, const char* currentVersion) {
    _updateServer = updateServer;
    _versionCheckUrl = versionCheckUrl;
    _currentVersion = currentVersion;
    _wifi = new WIFILib(ssid, password);
}

/**
 * @brief 析构函数实现
 * @details 清理资源，释放内存
 */
OTAUpdate::~OTAUpdate() {
    if (_wifi != nullptr) {
        delete _wifi;
        _wifi = nullptr;
    }
}

/**
 * @brief 初始化OTA更新
 * @return true: 初始化成功, false: 初始化失败
 */
bool OTAUpdate::begin() {
    return _wifi->begin();
}

/**
 * @brief 启动OTA任务
 * @details 创建并启动OTA更新检查任务
 */
void OTAUpdate::startOTATask() {
    Serial.println("OTA任务启动");
    
    // 等待WiFi连接
    int retryCount = 0;
    while (!_wifi->isWiFiConnected() && retryCount < 10) {
        Serial.println("等待WiFi连接...");
        vTaskDelay(pdMS_TO_TICKS(1000));
        retryCount++;
    }
    
    if (!_wifi->isWiFiConnected()) {
        Serial.println("OTA任务：WiFi未连接，任务结束");
        return;
    }
    
    // 等待获取IP地址
    retryCount = 0;
    while (_wifi->getLocalIP().toString() == "0.0.0.0" && retryCount < 10) {
        Serial.println("等待获取IP地址...");
        vTaskDelay(pdMS_TO_TICKS(1000));
        retryCount++;
    }
    
    if (_wifi->getLocalIP().toString() == "0.0.0.0") {
        Serial.println("OTA任务：未能获取IP地址，任务结束");
        return;
    }
    
    vTaskDelay(pdMS_TO_TICKS(2000));  // 等待网络稳定
    
    Serial.println("开始OTA更新检查...");
    
    if (checkForUpdates()) {
        Serial.println("OTA更新检查成功");
    } else {
        Serial.println("OTA更新检查失败");
    }
    
    Serial.println("OTA任务完成");
}

/**
 * @brief 安全的HTTP请求
 * @param url 请求URL
 * @param timeout 超时时间(毫秒)
 * @return HTTP响应码
 * @details 带有重试机制的HTTP请求
 */
int OTAUpdate::safeHttpRequest(const char* url, int timeout) {
    _http.begin(url);
    _http.setTimeout(timeout);
    
    // 设置重试次数
    int retryCount = 0;
    int httpCode = -1;
    
    while (retryCount < 3 && httpCode < 0) {
        httpCode = _http.GET();
        if (httpCode < 0) {
            Serial.printf("HTTP请求失败 (第%d次): %s\n", retryCount + 1, _http.errorToString(httpCode).c_str());
            retryCount++;
            delay(1000);  // 等待1秒后重试
        }
    }
    
    return httpCode;
}

/**
 * @brief 检查更新
 * @return true: 检查成功, false: 检查失败
 * @details 检查是否有新版本可用
 */
bool OTAUpdate::checkForUpdates() {
    Serial.println("开始检查更新...");
    
    // 确保网络连接正常
    if (!_wifi->checkNetworkConnection()) {
        Serial.println("网络连接异常，无法检查更新");
        return false;
    }
    
    // 尝试获取版本信息
    int httpCode = safeHttpRequest(_versionCheckUrl);
    if (httpCode != HTTP_CODE_OK) {
        Serial.printf("版本检查失败，HTTP错误码: %d\n", httpCode);
        return false;
    }
    
    String payload = _http.getString();
    _http.end();
    
    Serial.println("收到版本信息响应：");
    Serial.println(payload);
    
    // 解析JSON响应
    DynamicJsonDocument doc(1024);
    DeserializationError error = deserializeJson(doc, payload);
    
    if (error) {
        Serial.print("JSON解析失败: ");
        Serial.println(error.c_str());
        return false;
    }
    
    const char* latestVersion = doc["version"];
    const char* firmwareUrl = doc["url"];
    
    if (!latestVersion || !firmwareUrl) {
        Serial.println("版本信息不完整");
        return false;
    }
    
    Serial.printf("当前版本: %s\n", _currentVersion);
    Serial.printf("最新版本: %s\n", latestVersion);
    Serial.printf("固件下载地址: %s\n", firmwareUrl);
    
    // 比较版本
    if (strcmp(_currentVersion, latestVersion) < 0) {
        Serial.println("发现新版本，准备更新...");
        // 更新固件下载地址
        _updateServer = firmwareUrl;
        return performUpdate();
    }
    
    Serial.println("当前已是最新版本");
    return true;
}

/**
 * @brief 执行更新
 * @return true: 更新成功, false: 更新失败
 * @details 下载并安装新版本固件
 */
bool OTAUpdate::performUpdate() {
    Serial.println("开始执行更新...");
    
    // 确保网络连接正常
    if (!_wifi->checkNetworkConnection()) {
        Serial.println("网络连接异常，无法执行更新");
        return false;
    }
    
    Serial.printf("正在从 %s 下载固件...\n", _updateServer);
    
    // 尝试下载并更新固件
    int httpCode = safeHttpRequest(_updateServer);
    if (httpCode != HTTP_CODE_OK) {
        Serial.printf("固件下载失败，HTTP错误码: %d\n", httpCode);
        return false;
    }
    
    // 获取固件大小
    int contentLength = _http.getSize();
    if (contentLength <= 0) {
        Serial.println("无效的固件大小");
        return false;
    }
    
    Serial.printf("固件大小: %d 字节\n", contentLength);
    
    // 检查可用存储空间
    uint32_t freeSpace = ESP.getFreeSketchSpace();
    Serial.printf("可用存储空间: %d 字节\n", freeSpace);
    
    if (freeSpace < contentLength) {
        Serial.println("存储空间不足，尝试清理...");
        // 尝试清理一些空间
        if (LittleFS.begin(true)) {  // true表示如果LittleFS未格式化则进行格式化
            LittleFS.format();
            LittleFS.end();
            Serial.println("已清理LittleFS分区");
            
            // 重新检查空间
            freeSpace = ESP.getFreeSketchSpace();
            Serial.printf("清理后可用空间: %d 字节\n", freeSpace);
        } else {
            Serial.println("LittleFS初始化失败");
        }
        
        if (freeSpace < contentLength) {
            Serial.println("存储空间仍然不足，无法更新");
            return false;
        }
    }
    
    // 准备更新
    if (!Update.begin(contentLength)) {
        Serial.printf("无法开始更新，空间不足。需要: %d 字节\n", contentLength);
        return false;
    }
    
    // 设置更新进度回调
    Update.onProgress([](unsigned int progress, unsigned int total) {
        static unsigned int lastPercent = 0;
        unsigned int percent = (progress * 100) / total;
        if (percent != lastPercent) {
            Serial.printf("更新进度: %u%%\n", percent);
            lastPercent = percent;
        }
    });
    
    // 写入固件
    size_t written = Update.writeStream(*_http.getStreamPtr());
    if (written != contentLength) {
        Serial.printf("写入固件失败，已写入: %d 字节，预期: %d 字节\n", written, contentLength);
        return false;
    }
    
    // 完成更新
    if (!Update.end()) {
        Serial.printf("更新失败，错误码: %d\n", Update.getError());
        return false;
    }
    
    Serial.println("更新成功，即将重启...");
    delay(1000);
    ESP.restart();
    return true;
} 